#include <rclcpp/rclcpp.hpp>
#include <std_msgs/msg/string.hpp>
#include <opentelemetry/trace/provider.h>
#include <opentelemetry/exporters/ostream/span_exporter.h>
#include <opentelemetry/sdk/trace/simple_processor.h>
#include <opentelemetry/sdk/trace/tracer_provider.h>
#include <opentelemetry/sdk/trace/samplers/always_on.h>
#include <opentelemetry/sdk/resource/resource.h>
#include <opentelemetry/nostd/shared_ptr.h>
#include <opentelemetry/nostd/unique_ptr.h>

#include <chrono>
#include <thread>
#include <string>

namespace trace_api = opentelemetry::trace;
namespace trace_sdk = opentelemetry::sdk::trace;
namespace resource_sdk = opentelemetry::sdk::resource;
namespace nostd = opentelemetry::nostd;

// Initialize OpenTelemetry tracing
void InitTracer() {
    auto exporter = std::unique_ptr<trace_sdk::SpanExporter>(
        new opentelemetry::exporter::trace::OStreamSpanExporter);
    
    auto processor = std::unique_ptr<trace_sdk::SpanProcessor>(
        new trace_sdk::SimpleSpanProcessor(std::move(exporter)));
    
    auto resource = resource_sdk::Resource::Create({
        {"service.name", "ros2-processor-node"},
        {"service.version", "1.0.0"},
        {"service.instance.id", "processor-001"}
    });
    
    auto sampler = std::unique_ptr<trace_sdk::Sampler>(
        new trace_sdk::AlwaysOnSampler);
    
    auto provider = nostd::shared_ptr<trace_api::TracerProvider>(
        new trace_sdk::TracerProvider(std::move(processor), resource, std::move(sampler)));
    
    trace_api::Provider::SetTracerProvider(provider);
}

class ProcessorNode : public rclcpp::Node {
public:
    ProcessorNode() : Node("processor_node") {
        // Initialize OpenTelemetry
        InitTracer();
        tracer_ = trace_api::Provider::GetTracerProvider()->GetTracer("processor-node");
        
        // Create subscriber
        subscription_ = this->create_subscription<std_msgs::msg::String>(
            "data_topic", 10,
            std::bind(&ProcessorNode::messageCallback, this, std::placeholders::_1));
        
        // Create publisher
        publisher_ = this->create_publisher<std_msgs::msg::String>("processed_topic", 10);
        
        RCLCPP_INFO(this->get_logger(), "Processor Node started with OpenTelemetry tracing");
        RCLCPP_INFO(this->get_logger(), "This node will create independent trace_id for each message");
    }

private:
    void messageCallback(const std_msgs::msg::String::SharedPtr msg) {
        // Create root span for message processing (independent trace)
        auto span = tracer_->StartSpan("process_data_message", {
            {"node.name", "processor_node"},
            {"topic.input", "data_topic"},
            {"topic.output", "processed_topic"},
            {"message.type", "std_msgs/String"},
            {"operation.type", "process"},
            {"message.count", static_cast<int64_t>(message_count_)}
        });
        
        auto scope = tracer_->WithActiveSpan(span);
        
        try {
            RCLCPP_INFO(this->get_logger(), "Received: %s", msg->data.c_str());
            RCLCPP_INFO(this->get_logger(), "Processor Node - Processing message #%zu", message_count_);
            
            // Add span attributes
            span->SetAttribute("input.message", msg->data);
            span->AddEvent("message_received");
            
            // Simulate processing time
            std::this_thread::sleep_for(std::chrono::milliseconds(80));
            
            // Process message (add some transformation)
            auto processed_msg = std_msgs::msg::String();
            processed_msg.data = "Processed: " + msg->data;
            
            span->SetAttribute("output.message", processed_msg.data);
            span->AddEvent("message_processed");
            
            // Simulate more processing time
            std::this_thread::sleep_for(std::chrono::milliseconds(50));
            
            // Publish processed message
            publisher_->publish(processed_msg);
            span->AddEvent("message_forwarded");
            
            // Set success status
            span->SetStatus(trace_api::StatusCode::kOk, "Message processed and forwarded successfully");
            
            RCLCPP_INFO(this->get_logger(), "Published: %s", processed_msg.data.c_str());
            
            message_count_++;
            
        } catch (const std::exception& e) {
            span->SetStatus(trace_api::StatusCode::kError, e.what());
            span->SetAttribute("error.message", e.what());
            RCLCPP_ERROR(this->get_logger(), "Error processing message: %s", e.what());
        }
        
        span->End();
    }

    rclcpp::Subscription<std_msgs::msg::String>::SharedPtr subscription_;
    rclcpp::Publisher<std_msgs::msg::String>::SharedPtr publisher_;
    nostd::shared_ptr<trace_api::Tracer> tracer_;
    size_t message_count_ = 0;
};

int main(int argc, char* argv[]) {
    rclcpp::init(argc, argv);
    
    auto node = std::make_shared<ProcessorNode>();
    
    RCLCPP_INFO(node->get_logger(), "Starting Processor Node with OpenTelemetry tracing...");
    RCLCPP_INFO(node->get_logger(), "Note: Each node creates independent trace_id (no cross-node tracing)");
    
    rclcpp::spin(node);
    rclcpp::shutdown();
    
    return 0;
}
